#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>

#include <linux/module.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/acpi.h>
#include <crypto/internal/hash.h>
#include <linux/crypto.h>
#include <crypto/hash.h>
#include <crypto/internal/skcipher.h>
#include <crypto/internal/akcipher.h>
#include <linux/ioport.h>
#include <linux/io.h>

MODULE_LICENSE("GPL");

char *alg = NULL;
u32 len = 1024;

module_param(alg, charp, 0);
module_param(len, uint, 0);

#define get_driver_name(tfm_type, tfm) crypto_tfm_alg_driver_name(tfm_type ## _tfm(tfm))


static void print_buf(u8 *name, u8 *buf, u32 len)
{
	u32 i;

	printk(KERN_ERR"%s[0x%x]:\n", name, len);
	for (i = 0; i < len; i += 16) {
		printk(KERN_ERR"%08x: %02x %02x %02x %02x %02x %02x %02x %02x "
							"%02x %02x %02x %02x %02x %02x %02x %02x\n", i,
							buf[i + 0], buf[i + 1], buf[i + 2], buf[i + 3], buf[i + 4], buf[i + 5], buf[i + 6], buf[i + 7],
							buf[i + 8], buf[i + 9], buf[i + 10], buf[i + 11], buf[i + 12], buf[i + 13], buf[i + 14], buf[i + 15]);
	}
}

static void hash_test(char *alg)
{   
    u8 *src;
    u8 dgst[32] = {0};
    struct scatterlist sg;
	struct crypto_wait wait;
	struct ahash_request *req;
	struct crypto_ahash *tfm;
	int ret; 


    // 随机申请一段空间
    src = kmalloc(len, GFP_KERNEL);
    if (src == NULL) {
		printk(KERN_ERR"%s:kmalloc failed!\n", __func__);
		return;
	}


    // TODO: fill data to src

    tfm = crypto_alloc_ahash(alg, 0, 0);
	if (IS_ERR(tfm)) {
		printk(KERN_ERR"failed to load transform for %s: %ld\n", alg, PTR_ERR(tfm));
		goto exit;
	}

    printk(KERN_INFO "\ndgst testing of async %s (%s)\n", alg, get_driver_name(crypto_ahash, tfm));

    sg_init_table(&sg, 1);
	sg_set_buf(&sg, src, len);

    req = ahash_request_alloc(tfm, GFP_KERNEL);
	if (!req) {
		printk(KERN_ERR"ahash request allocation failure\n");
		goto fail;
	}

    crypto_init_wait(&wait);
	ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait);

	ahash_request_set_crypt(req, &sg, dgst, len);

	ret = crypto_wait_req(crypto_ahash_digest(req), &wait);
	if (ret) {
		printk(KERN_ERR"hashing failed ret=%d\n", ret);
	}

    // print_buf("data", src, len);
    print_buf(alg, dgst, 32);
	ahash_request_free(req);

fail:
    crypto_free_ahash(tfm);

exit:
    kfree(src);
}

static void aes_test(char *alg)
{
    u8 *src, *dst;
    u8 key[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf};
    u8 iv[16]= {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf};

    struct crypto_wait wait;
	struct skcipher_request *req;
	struct crypto_skcipher *tfm;
	struct scatterlist ssg, dsg;
    int ret;


    src = kmalloc(len * 2, GFP_KERNEL);
    if (src == NULL) {
		printk(KERN_ERR"%s:kmalloc failed!\n", __func__);
		return;
	}
    dst = src+len;

    // TODO: fill data to src
    tfm = crypto_alloc_skcipher(alg, 0, 0);
	if (IS_ERR(tfm)) {
		printk(KERN_ERR"failed to load transform for %s: %ld\n", alg, PTR_ERR(tfm));
        kfree(src);
		return;
	}

    printk(KERN_INFO "\nsingle testing of async %s (%s) enc!\n", alg, get_driver_name(crypto_skcipher, tfm));

    req = skcipher_request_alloc(tfm, GFP_KERNEL);
	if (!req) {
		printk(KERN_INFO"skcipher: Failed to allocate request for %s\n", alg);
		goto fail;
	}

    crypto_init_wait(&wait);
	skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait);

	crypto_skcipher_clear_flags(tfm, ~0);
	crypto_skcipher_setkey(tfm, key, 16);

	sg_init_table(&ssg, 1);
	sg_set_buf(&ssg, src, len);
	sg_init_table(&dsg, 1);
	sg_set_buf(&dsg, dst, len);
	skcipher_request_set_crypt(req, &ssg, &dsg, len, iv);

    ret = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
	if (ret) {
		printk(KERN_ERR"crypto_skcipher_encrypt failed ret=%d\n", ret);
	}
			
	skcipher_request_free(req);

    print_buf(alg, dst, len);
fail:
	crypto_free_skcipher(tfm);

    kfree(src);
}

static int crypto_example_init(void)
{
    if (!alg)
    {
        printk(KERN_ERR "Null alg, please specify alg name\n");
        return -1;
    }

    if (0 == strcmp(alg, "md5") || 0 == strcmp(alg, "sha1")) {
        hash_test(alg);
    } else if (0 == strcmp(alg, "aes")) {
        aes_test("cbc(aes)");
    } else {
        printk(KERN_ERR "No example for alg %s\n", alg);
    }



    return -1; // 只是一个测试模块，不需要安装成功
}


static void crypto_example_exit(void)
{
    printk(KERN_ALERT"Goodbye, %s!\n", alg);
}

module_init(crypto_example_init);
module_exit(crypto_example_exit);

MODULE_DESCRIPTION("Module named hello.");

